package io.jft.doll.tcp

import android.annotation.SuppressLint
import com.g.base.extend.mainIoSchedulers
import com.g.base.extend.retryWithDelay
import com.g.base.help.d
import io.jft.doll.tcp.event.TcpData
import io.jft.doll.tcp.handle.BusinessHandle
import io.jft.doll.tcp.handle.DecoderHandle
import io.jft.doll.tcp.handle.EncoderHandle
import io.netty.bootstrap.Bootstrap
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import io.netty.channel.Channel
import io.netty.channel.ChannelFuture
import io.netty.channel.ChannelInitializer
import io.netty.channel.ChannelOption
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.nio.NioSocketChannel
import io.netty.handler.logging.LogLevel
import io.netty.handler.logging.LoggingHandler
import io.netty.handler.timeout.IdleStateHandler
import io.reactivex.Observable
import java.lang.RuntimeException
import java.util.concurrent.TimeUnit


/**
 * Created by G on 2017/12/25 0025.
 */
val tcpClient by lazy { TcpClient() }

class TcpClient {
    enum class ConnectStatus {
        Offline,
        OfflineIng,
        ConnectIng,
        Connected,
        Login
    }

    var status: ConnectStatus = ConnectStatus.Offline
    private var channel: Channel? = null
    private val bootstrap: Bootstrap by lazy {
        Bootstrap()
                .group(NioEventLoopGroup())
                .channel(NioSocketChannel::class.java)
                .handler(LoggingHandler(LogLevel.DEBUG))
                .handler(object : ChannelInitializer<Channel>() {
                    override fun initChannel(ch: Channel?) {
                        val pipeline = ch?.pipeline()
                        pipeline?.addLast("IdleStateHandler", IdleStateHandler(5, 5, 20, TimeUnit.SECONDS))
                        pipeline?.addLast("DecoderHandle", DecoderHandle())
                        pipeline?.addLast("EncoderHandle", EncoderHandle())
                        pipeline?.addLast("BusinessHandle", BusinessHandle())
                    }
                })
                .option(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.TCP_NODELAY, true)
    }

    fun connect() {
        if (status != ConnectStatus.Offline) return
        d("tcp connect start")
        if (channel != null && channel?.isActive == true) {
            d("tcp connect close before")
            channel?.close()
        }
        tcpClient.status = ConnectStatus.ConnectIng
        val channelFuture: ChannelFuture = bootstrap.connect(TcpC.TCP_SERVER, TcpC.TCP_SERVER_PORT)
        channel = channelFuture.channel()
        channelFuture.addListener {
            if (!it.isSuccess) {
                d("tcp connect error : ${it.cause()}")
                tcpClient.status = ConnectStatus.Offline
                Thread.sleep(TcpC.RECONNECT_TIME)
                connect()
            }
        }
    }

    fun disConnect() {
        d("tcp disConnect")
        if (status < ConnectStatus.Connected) return
        tcpClient.status = ConnectStatus.Offline
        channel?.disconnect()
                ?.addListener {
                    d("tcp disConnect succeed: ${it.isSuccess}")
                    if (it.isSuccess) {
                        channel?.pipeline()?.remove("BusinessHandle")
                    } else {
                        channel?.close()
                    }
                }
    }

    fun createBuffer(): ByteBuf = Unpooled.buffer()

    @SuppressLint("CheckResult")
    fun writeAndFlush(tcpData: TcpData, needLogin: Boolean = true, callBack: (String) -> Unit) {
        Observable.create<String> {
            when {
                tcpClient.status == ConnectStatus.ConnectIng -> {
                    it.onError(RuntimeException("正在重新连接中 请稍后再试吧~"))
                }
                tcpClient.status == ConnectStatus.Offline -> {
                    connect()
                    it.onError(RuntimeException("开始重新连接服务器 请稍后再试吧~"))
                }
                tcpClient.status == ConnectStatus.OfflineIng -> {
                    it.onError(RuntimeException("正在断开服务器连接 请稍后再试~"))
                }
                needLogin && tcpClient.status == ConnectStatus.Connected -> {
                    tcpClient.connect()
                    it.onError(RuntimeException("登录服务器失败 , 正在努力恢复中连接! 请稍后再试吧"))
                }
                else -> {
                    channel!!.writeAndFlush(tcpData).addListener { future ->
                        if (future.isSuccess) {
                            it.onNext("")
                            it.onComplete()
                        } else {
                            it.onError(RuntimeException("无法发送请求~!"))
                        }
                    }
                }
            }
        }
                .retryWithDelay(3, 500L)
                .mainIoSchedulers()
                .subscribe({ callBack(it) }, { callBack(it.message ?: "请求错误~") })
    }
}

